# -*- coding: utf-8 -*-
import os
import urllib.parse
from typing import Optional
from fastapi import Request, UploadFile, File, Form, HTTPException
from fastapi.responses import StreamingResponse

from schemas.response_entity import JsonModel
from schemas.material_schema import (
    MaterialTypeCreateRequest, MaterialTypeUpdateRequest, MaterialTypeResponse,
    FileSourceCreateRequest, FileSourceUpdateRequest, FileSourceResponse,
    MaterialCreateRequest, MaterialUpdateRequest, MaterialResponse, MaterialListResponse,
    MaterialQueryRequest, MaterialUploadRequest, MaterialBatchGetRequest, MaterialFuzzySearchRequest
)
from db.repository.material import (
    db_create_material_type, db_get_material_type_by_id, db_get_all_material_types,
    db_update_material_type, db_delete_material_type,
    db_create_file_source, db_get_file_source_by_id, db_get_all_file_sources,
    db_update_file_source, db_delete_file_source,
    db_create_material, db_get_material_by_id, db_get_material_by_hash,
    db_get_materials, db_get_materials_by_ids, db_get_materials_with_fuzzy_search,
    db_update_material, db_delete_material, db_physical_delete_material
)
from utils.minio_utils import minio_service, get_file_hash
from utils.file_utils import normalize_filename_extension


# =========================== 资料类型字典 ================================

async def create_material_type(request: Request, material_type_req: MaterialTypeCreateRequest):
    """创建资料类型"""
    try:
        material_type = db_create_material_type(name=material_type_req.name)
        if material_type:
            return JsonModel(
                code=200, 
                data=MaterialTypeResponse(**material_type.to_dict()), 
                msg="创建成功"
            ).to_response()
        else:
            return JsonModel(code=400, data=None, msg="创建失败").to_response()
    except Exception as e:
        return JsonModel(code=500, data=None, msg=f"创建失败: {str(e)}").to_response()


async def get_material_type_by_id(request: Request, type_id: int):
    """根据ID获取资料类型"""
    try:
        material_type = db_get_material_type_by_id(type_id=type_id)
        if material_type:
            return JsonModel(
                code=200, 
                data=MaterialTypeResponse(**material_type.to_dict()), 
                msg="获取成功"
            ).to_response()
        else:
            return JsonModel(code=404, data=None, msg="资料类型不存在").to_response()
    except Exception as e:
        return JsonModel(code=500, data=None, msg=f"获取失败: {str(e)}").to_response()


async def get_all_material_types(request: Request):
    """获取所有资料类型"""
    try:
        material_types = db_get_all_material_types()
        data = [MaterialTypeResponse(**mt.to_dict()) for mt in material_types]
        return JsonModel(code=200, data=data, msg="获取成功").to_response()
    except Exception as e:
        return JsonModel(code=500, data=None, msg=f"获取失败: {str(e)}").to_response()


async def update_material_type(request: Request, type_id: int, material_type_req: MaterialTypeUpdateRequest):
    """更新资料类型"""
    try:
        material_type = db_update_material_type(type_id=type_id, name=material_type_req.name)
        if material_type:
            return JsonModel(
                code=200, 
                data=MaterialTypeResponse(**material_type.to_dict()), 
                msg="更新成功"
            ).to_response()
        else:
            return JsonModel(code=404, data=None, msg="资料类型不存在").to_response()
    except Exception as e:
        return JsonModel(code=500, data=None, msg=f"更新失败: {str(e)}").to_response()


async def delete_material_type(request: Request, type_id: int):
    """删除资料类型"""
    try:
        success = db_delete_material_type(type_id=type_id)
        if success:
            return JsonModel(code=200, data=None, msg="删除成功").to_response()
        else:
            return JsonModel(code=404, data=None, msg="资料类型不存在").to_response()
    except Exception as e:
        return JsonModel(code=500, data=None, msg=f"删除失败: {str(e)}").to_response()


# =========================== 文件来源字典 ================================

async def create_file_source(request: Request, file_source_req: FileSourceCreateRequest):
    """创建文件来源"""
    try:
        file_source = db_create_file_source(name=file_source_req.name)
        if file_source:
            return JsonModel(
                code=200, 
                data=FileSourceResponse(**file_source.to_dict()), 
                msg="创建成功"
            ).to_response()
        else:
            return JsonModel(code=400, data=None, msg="创建失败").to_response()
    except Exception as e:
        return JsonModel(code=500, data=None, msg=f"创建失败: {str(e)}").to_response()


async def get_file_source_by_id(request: Request, source_id: int):
    """根据ID获取文件来源"""
    try:
        file_source = db_get_file_source_by_id(source_id=source_id)
        if file_source:
            return JsonModel(
                code=200, 
                data=FileSourceResponse(**file_source.to_dict()), 
                msg="获取成功"
            ).to_response()
        else:
            return JsonModel(code=404, data=None, msg="文件来源不存在").to_response()
    except Exception as e:
        return JsonModel(code=500, data=None, msg=f"获取失败: {str(e)}").to_response()


async def get_all_file_sources(request: Request):
    """获取所有文件来源"""
    try:
        file_sources = db_get_all_file_sources()
        data = [FileSourceResponse(**fs.to_dict()) for fs in file_sources]
        return JsonModel(code=200, data=data, msg="获取成功").to_response()
    except Exception as e:
        return JsonModel(code=500, data=None, msg=f"获取失败: {str(e)}").to_response()


async def update_file_source(request: Request, source_id: int, file_source_req: FileSourceUpdateRequest):
    """更新文件来源"""
    try:
        file_source = db_update_file_source(source_id=source_id, name=file_source_req.name)
        if file_source:
            return JsonModel(
                code=200, 
                data=FileSourceResponse(**file_source.to_dict()), 
                msg="更新成功"
            ).to_response()
        else:
            return JsonModel(code=404, data=None, msg="文件来源不存在").to_response()
    except Exception as e:
        return JsonModel(code=500, data=None, msg=f"更新失败: {str(e)}").to_response()


async def delete_file_source(request: Request, source_id: int):
    """删除文件来源"""
    try:
        success = db_delete_file_source(source_id=source_id)
        if success:
            return JsonModel(code=200, data=None, msg="删除成功").to_response()
        else:
            return JsonModel(code=404, data=None, msg="文件来源不存在").to_response()
    except Exception as e:
        return JsonModel(code=500, data=None, msg=f"删除失败: {str(e)}").to_response()


# =========================== 资料表 ================================

async def upload_material(
    request: Request,
    file: UploadFile = File(...),
    file_topic: str = Form(...),
    company: Optional[str] = Form(None),
    material_type: int = Form(...),
    file_source: Optional[int] = Form(None)
):
    """上传资料文件"""
    try:
        # 检查文件是否为空
        if not file or file.size == 0:
            return JsonModel(code=400, data=None, msg="文件不能为空").to_response()
        
        # 计算文件哈希
        file_hash = await get_file_hash(file)
        
        # 检查文件是否已存在
        existing_material = db_get_material_by_hash(file_hash=file_hash)
        if existing_material:
            # 获取关联信息（在数据库会话关闭前）
            material_dict = existing_material.to_dict()
            if existing_material.material_type_rel:
                material_dict["material_type_name"] = existing_material.material_type_rel.name
            if existing_material.file_source_rel:
                material_dict["file_source_name"] = existing_material.file_source_rel.name

            # 获取下载URL
            download_url = minio_service.get_file_url(existing_material.file_path)
            material_dict["download_url"] = download_url
            return JsonModel(
                code=200,
                data=MaterialResponse(**material_dict),
                msg="上传成功"
            ).to_response()
        
        # 生成文件路径
        normalized_filename = normalize_filename_extension(file.filename)
        file_extension = os.path.splitext(normalized_filename)[1]
        file_name = f"{file_hash}{file_extension}"
        object_name = f"materials/{file_name}"
        
        # 上传文件到minio
        upload_success = await minio_service.upload_file(file, object_name)
        if not upload_success:
            return JsonModel(code=500, data=None, msg="文件上传失败").to_response()
        
        # 创建资料记录
        material_data = {
            "file_topic": file_topic,
            "file_name": normalized_filename,
            "company": company,
            "file_path": object_name,
            "file_type": file_extension.lstrip('.'),
            "material_type": material_type,
            "file_size": file.size,
            "duration": 0,  # 暂时设为0，后续可以解析视频时长
            "file_source": file_source,
            "file_hash": file_hash
        }
        
        material = db_create_material(material_data)
        if material:
            # 获取关联信息（在数据库会话关闭前）
            material_dict = material.to_dict()
            if material.material_type_rel:
                material_dict["material_type_name"] = material.material_type_rel.name
            if material.file_source_rel:
                material_dict["file_source_name"] = material.file_source_rel.name
            
            # 获取下载URL
            download_url = minio_service.get_file_url(object_name)
            material_dict["download_url"] = download_url
            
            return JsonModel(
                code=200, 
                data=MaterialResponse(**material_dict), 
                msg="上传成功"
            ).to_response()
        else:
            # 如果数据库插入失败，删除已上传的文件
            minio_service.delete_file(object_name)
            return JsonModel(code=500, data=None, msg="资料记录创建失败").to_response()
            
    except Exception as e:
        return JsonModel(code=500, data=None, msg=f"上传失败: {str(e)}").to_response()


async def get_material_by_id(request: Request, material_id: int):
    """根据ID获取资料"""
    try:
        material = db_get_material_by_id(material_id=material_id)
        if material:
            material_dict = material.to_dict()
            if material.material_type_rel:
                material_dict["material_type_name"] = material.material_type_rel.name
            if material.file_source_rel:
                material_dict["file_source_name"] = material.file_source_rel.name
            
            # 获取下载URL
            download_url = minio_service.get_file_url(material.file_path)
            material_dict["download_url"] = download_url
            
            return JsonModel(
                code=200, 
                data=MaterialResponse(**material_dict), 
                msg="获取成功"
            ).to_response()
        else:
            return JsonModel(code=404, data=None, msg="资料不存在").to_response()
    except Exception as e:
        return JsonModel(code=500, data=None, msg=f"获取失败: {str(e)}").to_response()


async def get_materials(request: Request, query_req: MaterialQueryRequest):
    """分页获取资料列表"""
    try:
        result = db_get_materials(
            page=query_req.page,
            page_size=query_req.page_size,
            file_topic=query_req.file_topic,
            company=query_req.company,
            material_type=query_req.material_type,
            file_source=query_req.file_source
        )
        
        # 处理材料列表
        materials = []
        for material in result["materials"]:
            material_dict = material.to_dict()
            if material.material_type_rel:
                material_dict["material_type_name"] = material.material_type_rel.name
            if material.file_source_rel:
                material_dict["file_source_name"] = material.file_source_rel.name
            
            # 获取下载URL
            download_url = minio_service.get_file_url(material.file_path)
            material_dict["download_url"] = download_url
            
            materials.append(MaterialResponse(**material_dict))
        
        response_data = MaterialListResponse(
            materials=materials,
            total=result["total"],
            page=result["page"],
            page_size=result["page_size"],
            total_pages=result["total_pages"]
        )
        
        return JsonModel(code=200, data=response_data.dict(), msg="获取成功").to_response()
    except Exception as e:
        return JsonModel(code=500, data=None, msg=f"获取失败: {str(e)}").to_response()


async def get_materials_fuzzy_search(request: Request, query_req: MaterialFuzzySearchRequest):
    """分页获取资料列表，支持模糊查询"""
    try:
        result = db_get_materials_with_fuzzy_search(
            page=query_req.page,
            page_size=query_req.page_size,
            file_topic=query_req.file_topic,
            company=query_req.company,
            material_type=query_req.material_type,
            file_source=query_req.file_source,
            search_keyword=query_req.search_keyword
        )
        
        # 处理材料列表
        materials = []
        for material in result["materials"]:
            material_dict = material.to_dict()
            if material.material_type_rel:
                material_dict["material_type_name"] = material.material_type_rel.name
            if material.file_source_rel:
                material_dict["file_source_name"] = material.file_source_rel.name
            
            # 获取下载URL
            download_url = minio_service.get_file_url(material.file_path)
            material_dict["download_url"] = download_url
            
            materials.append(MaterialResponse(**material_dict))
        
        response_data = MaterialListResponse(
            materials=materials,
            total=result["total"],
            page=result["page"],
            page_size=result["page_size"],
            total_pages=result["total_pages"]
        )
        
        return JsonModel(code=200, data=response_data.dict(), msg="获取成功").to_response()
    except Exception as e:
        return JsonModel(code=500, data=None, msg=f"获取失败: {str(e)}").to_response()


async def get_materials_by_ids(request: Request, batch_req: MaterialBatchGetRequest):
    """根据ID数组批量获取资料列表"""
    try:
        if not batch_req.material_ids:
            return JsonModel(code=400, data=None, msg="资料ID列表不能为空").to_response()
        
        materials = db_get_materials_by_ids(material_ids=batch_req.material_ids)
        
        # 处理材料列表
        material_list = []
        for material in materials:
            material_dict = material.to_dict()
            if material.material_type_rel:
                material_dict["material_type_name"] = material.material_type_rel.name
            if material.file_source_rel:
                material_dict["file_source_name"] = material.file_source_rel.name
            
            # 获取下载URL
            download_url = minio_service.get_file_url(material.file_path)
            material_dict["download_url"] = download_url
            
            material_list.append(MaterialResponse(**material_dict))
        
        return JsonModel(code=200, data=material_list, msg="获取成功").to_response()
    except Exception as e:
        return JsonModel(code=500, data=None, msg=f"获取失败: {str(e)}").to_response()


async def update_material(request: Request, material_id: int, material_req: MaterialUpdateRequest):
    """更新资料"""
    try:
        # 过滤掉None值
        update_data = {k: v for k, v in material_req.dict().items() if v is not None}
        
        material = db_update_material(material_id=material_id, update_data=update_data)
        if material:
            material_dict = material.to_dict()
            if material.material_type_rel:
                material_dict["material_type_name"] = material.material_type_rel.name
            if material.file_source_rel:
                material_dict["file_source_name"] = material.file_source_rel.name
            
            # 获取下载URL
            download_url = minio_service.get_file_url(material.file_path)
            material_dict["download_url"] = download_url
            
            return JsonModel(
                code=200, 
                data=MaterialResponse(**material_dict), 
                msg="更新成功"
            ).to_response()
        else:
            return JsonModel(code=404, data=None, msg="资料不存在").to_response()
    except Exception as e:
        return JsonModel(code=500, data=None, msg=f"更新失败: {str(e)}").to_response()


async def delete_material(request: Request, material_id: int):
    """删除资料（逻辑删除）"""
    try:
        success = db_delete_material(material_id=material_id)
        if success:
            return JsonModel(code=200, data=None, msg="删除成功").to_response()
        else:
            return JsonModel(code=404, data=None, msg="资料不存在").to_response()
    except Exception as e:
        return JsonModel(code=500, data=None, msg=f"删除失败: {str(e)}").to_response()


async def download_material(request: Request, material_id: int):
    """下载资料文件"""
    try:
        material = db_get_material_by_id(material_id=material_id)
        if not material:
            return JsonModel(code=404, data=None, msg="资料不存在").to_response()
        
        # 检查文件是否存在
        if not minio_service.file_exists(material.file_path):
            return JsonModel(code=404, data=None, msg="文件不存在").to_response()
        
        # 获取文件流
        try:
            file_stream = minio_service.client.get_object(
                bucket_name=minio_service.bucket_name,
                object_name=material.file_path
            )
            
            # 处理中文文件名编码
            filename = material.file_name
            try:
                # 尝试UTF-8编码
                encoded_filename = urllib.parse.quote(filename.encode('utf-8'))
                content_disposition = f"attachment; filename*=UTF-8''{encoded_filename}"
            except UnicodeEncodeError:
                # 如果UTF-8编码失败，使用ASCII编码
                content_disposition = f"attachment; filename={filename}"
            
            return StreamingResponse(
                file_stream,
                media_type="application/octet-stream",
                headers={"Content-Disposition": content_disposition}
            )
        except Exception as e:
            return JsonModel(code=500, data=None, msg=f"文件下载失败: {str(e)}").to_response()
            
    except Exception as e:
        return JsonModel(code=500, data=None, msg=f"下载失败: {str(e)}").to_response()
